package org.kl.bf.utils;

import java.util.Collection;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaBuilder.In;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Selection;

import org.apache.commons.lang3.StringUtils;
import org.springframework.data.jpa.domain.Specification;
import org.springside.modules.utils.Collections3;

import com.google.common.collect.Lists;

/**
 * @Author hqm
 * @CreateTime 2014年6月20日 上午10:01:38
 * @ChangeLog 2014-07-01 Qken 添加 ISNULL、NoTNULL
 *            表达式，由于KLSearchFilter会过滤空值，故使用时请赋个非空值。 2014-09-28 Qken 添加GTF、LTF表达式
 *            用于两个字段值比较大小 2014-10-20 Qken 添加EQF,NEQF表达式 判断两个字段相等或不相等 2015-02-27
 *            添加buildSpecification方法，不必在service层添加冗余代码
 */
public class KLDynamicSpecifications {

	public static <T> Specification<T> byKLSearchFilter(final Collection<KLSearchFilter> filters, final Class<T> entityClazz) {
		return new Specification<T>() {
			@Override
			public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
				if (Collections3.isNotEmpty(filters)) {
					Predicate[] predicates = buildPredicates(filters, root, builder);
					return builder.and(predicates);
				}
				return builder.conjunction();
			}
		};
	}

	public static <T> Specification<T> buildSpecification(Map<String, Object> searchParams, final Class<T> entityClazz) {
		Map<String, KLSearchFilter> filters = KLSearchFilter.parse(searchParams);
		Specification<T> spec = KLDynamicSpecifications.byKLSearchFilter(filters.values(), entityClazz);
		return spec;
	}

	public static <T> T sumByKLSearchFilter(final Collection<KLSearchFilter> filters, final Class<T> entityClazz,
			EntityManager entityManager, CriteriaBuilder criteriaBuilder, CriteriaQuery<T> criteriaQuery, Root<T> root,
			Selection<?>... selections) {
		Predicate[] predicates = buildPredicates(filters, root, criteriaBuilder);
		criteriaQuery.multiselect(selections);
		criteriaQuery.where(predicates);
		List<T> resultList = entityManager.createQuery(criteriaQuery).getResultList();
		if (Collections3.isEmpty(resultList)) {
			return null;
		} else {
			return resultList.get(0);
		}
	}

	public static <T> Predicate[] buildPredicates(final Collection<KLSearchFilter> filters, Root<T> root, CriteriaBuilder builder) {
		if (Collections3.isNotEmpty(filters)) {
			List<Predicate> predicateList = Lists.newArrayList();
			for (KLSearchFilter filter : filters) {
				// nested path translate, 如Task的名为"user.name"的filedName,
				// 转换为Task.user.name属性
				if (filter.fieldName.indexOf("+OR+") > -1) {
					String[] fields = StringUtils.split(filter.fieldName, "+OR+");
					Predicate[] orPredicates = new Predicate[fields.length];
					for (int i = 0; i < fields.length; i++) {
						orPredicates[i] = onePredicate(root, builder, filter, fields[i]);
					}
					predicateList.add(builder.or(orPredicates));
				} else {
					predicateList.add(onePredicate(root, builder, filter, filter.fieldName));
				}
			}

			// 将所有条件用 and 联合起来
			if (predicateList.size() > 0) {
				return predicateList.toArray(new Predicate[predicateList.size()]);
			}
		}
		return null;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	private static <T> Predicate onePredicate(Root<T> root, CriteriaBuilder builder, KLSearchFilter filter, String fieldName) {
		Predicate predicate = null;
		Path expression = buildPath(root, fieldName);
		// logic operator
		switch (filter.operator) {
			case EQ:
				predicate = builder.equal(expression, filter.value);
				break;
			case EQF:
				predicate = builder.equal(expression, (Path) root.get((String) filter.value));
				break;
			case NEQ:
				predicate = builder.notEqual(expression, filter.value);
				break;
			case NEQF:
				predicate = builder.notEqual(expression, (Path) root.get((String) filter.value));
				break;
			case LIKE:
				predicate = builder.like(expression, "%" + filter.value + "%");
				break;
			case NLIKE:
				predicate = builder.notLike(expression, "%" + filter.value + "%");
				break;
			case GT:
				predicate = builder.greaterThan(expression, (Comparable) filter.value);
				break;
			case LT:
				predicate = builder.lessThan(expression, (Comparable) filter.value);
				break;
			case GTE:
				predicate = builder.greaterThanOrEqualTo(expression, (Comparable) filter.value);
				break;
			case LTE:
				predicate = builder.lessThanOrEqualTo(expression, (Comparable) filter.value);
				break;
			case GTF:
				predicate = builder.greaterThan(expression, (Path) root.get((String) filter.value));
				break;
			case LTF:
				predicate = builder.lessThan(expression, (Path) root.get((String) filter.value));
				break;
			case ISNULL:
				predicate = builder.isNull(expression);
				break;
			case NOTNULL:
				predicate = builder.isNotNull(expression);
				break;
			case IN:
				In<Object> in = builder.in(expression);
				String[] values = filter.value.toString().split(",");
				for (String v : values) {
					in.value(KLConvertUtils.convertStringToObject(v, filter.javaType));
				}
				predicate = in;
				break;
			case NIN:
				In<Object> notIn = builder.in(expression);
				String[] notInValues = filter.value.toString().split(",");
				for (String v : notInValues) {
					notIn.value(KLConvertUtils.convertStringToObject(v, filter.javaType));
				}
				predicate = notIn.not();
				break;
		}
		return predicate;
	}

	@SuppressWarnings("rawtypes")
	public static <T> Path buildPath(Root<T> root, String fieldName) {
		String[] names = StringUtils.split(fieldName, ".");
		Path expression = root.get(names[0]);
		for (int i = 1; i < names.length; i++) {
			expression = expression.get(names[i]);
		}
		return expression;
	}
}